#include "luaWrapper.h"
#include <iostream>
#include <cstdlib>
#include <QDebug>

LuaWrapper::LuaWrapper()
  : mLua(luaL_newstate())
{
  luaL_openlibs(mLua);
}

LuaWrapper::~LuaWrapper()
{
  lua_close(mLua);
}

bool LuaWrapper::load(const std::string &filename)
{
  bool ret(false);
  if(luaL_loadfile(mLua, filename.c_str()))
  {
    warning(lua_tostring(mLua, -1));
  }
  else if(lua_pcall(mLua, 0, 0, 0))
  {
    warning("Failed to run %s", lua_tostring(mLua, -1));
  }
  else
  {
    ret = true;
  }
  return ret;
}

bool LuaWrapper::load(const QString &filename)
{
  return load(filename.toStdString());
}

bool LuaWrapper::load(const char *filename)
{
  return load(std::string(filename));
}

bool LuaWrapper::getTableField(int tableIndex, const std::string &field, std::string &target)
{
  bool ret(false);
  lua_getfield(mLua, tableIndex, field.c_str());
  if (lua_isstring(mLua, -1))
  {
    target = lua_tostring(mLua, -1);
    ret = true;
  }
  lua_pop(mLua, 1);
  return ret;
}

bool LuaWrapper::getTableField(int tableIndex, const std::string &field, QString &target)
{
  std::string temp;
  bool ret(getTableField(tableIndex, field, temp));
  target = temp.c_str();
  return ret;
}

bool LuaWrapper::getTableField(int tableIndex, const std::string &field, int &target)
{
  double temp;
  bool ret(getTableField(tableIndex, field, temp));
  target = temp;
  return ret;
}

bool LuaWrapper::getTableField(int tableIndex, const std::string &field, double &target)
{
  bool ret(false);
  lua_getfield(mLua, tableIndex, field.c_str());
  if (lua_isnumber(mLua, -1))
  {
    target = lua_tonumber(mLua, -1);
    ret = true;
  }
  lua_pop(mLua, 1);
  return ret;
}

bool LuaWrapper::getTableTable(int tableIndex, const std::string &field)
{
  bool ret(false);
  lua_getfield(mLua, tableIndex, field.c_str());
  if (lua_istable(mLua, -1))
  {
    ret = true;
  }
  return ret;
}

bool LuaWrapper::initTableIterator()
{
  bool ret(false);
  if (!lua_istable(mLua, -1))
  {
    error("cannot find table");
  }
  else
  {
    lua_pushnil(mLua);
    ret = true;
  }
  return ret;
}

bool LuaWrapper::nextTableIteration()
{
  bool ret(false);
  if (!(lua_istable(mLua, -2) && lua_isnil(mLua, -1)) &&
      !lua_istable(mLua, -3))
  {
    error("table iterator not initialized");
  }
  else
  {
    if (!lua_isnil(mLua, -1))
    {
      lua_pop(mLua, 1);
    }
    ret = lua_next(mLua, -2);
  }
  return ret;
}

bool LuaWrapper::safetostring(int index, std::string &target)
{
  bool ret(false);
  lua_pushvalue(mLua, index);
  target = lua_tostring(mLua, -1);
  lua_pop(mLua, 1);
  return ret;
}

void LuaWrapper::printStack()
{
  qDebug() << "LuaWrapper::printStack";
  for(int i(1); i <= lua_gettop(mLua); ++i)
  {
    int type(lua_type(mLua, i));
    switch(type)
    {
    case LUA_TSTRING:
      qDebug() << i << ": string: " << lua_tostring(mLua, i);
      break;
    case LUA_TBOOLEAN:
      qDebug() << i << ": bool: " << lua_toboolean(mLua, i);
      break;
    case LUA_TNUMBER:
      qDebug() << i << ": number: " << lua_tonumber(mLua, i);
      break;
    default:
      qDebug() << i << ": type: " << lua_typename(mLua, i);
      break;
    }
  }
}

void LuaWrapper::error(const char* fmt, ...)
{
  va_list argp;
  va_start(argp, fmt);
  vfprintf(stderr, fmt, argp);
  std::cerr << std::endl;
  lua_close(mLua);
  exit(1);
}

void LuaWrapper::warning(const char* fmt, ...)
{
  va_list argp;
  va_start(argp, fmt);
  vfprintf(stderr, fmt, argp);
  std::cerr << std::endl;
  va_end(argp);
}
